#include "GraphicsCircularShapeItem.h"

#include "LeIpc7351/LeIpc7351.h"
#include "LeIpc7351/Primitives/CirclePrimitive.h"

#include "LeGraphicsView/LeGraphicsHandleItem.h"
#include "LeGraphicsView/LeGraphicsItemLayer.h"
#include "LeGraphicsView/LeGraphicsScene.h"

#include <QDebug>
#include <QStyleOptionGraphicsItem>

GraphicsCircularShapeItem::GraphicsCircularShapeItem(CirclePrimitive *primitive)
    : LeGraphicsItem(GftPad)
    , LDO::IDocumentObjectListener()
    , m_primitive(primitive)
{
    addHandles();
    updateGeometry();
    setPos(primitive->position());
    setRotation(primitive->rotation());
    setTransform(QTransform().scale(primitive->isMirrored() ? -1 : 1, 1));
    beginListeningToDocumentObject(primitive);
}

GraphicsCircularShapeItem::~GraphicsCircularShapeItem()
{
    endListeningToDocumentObject(m_primitive);
}

CirclePrimitive *GraphicsCircularShapeItem::primitive() const
{
    return m_primitive;
}

void GraphicsCircularShapeItem::updateGeometry()
{
    // Handles ignore view transfrom, so top is actually bottom ...

    handle(TopRightHandleRole)->setPos(boundingRect().bottomRight());
    handle(TopHandleRole)->setPos(QPointF(boundingRect().center().x(),
                                          boundingRect().bottom()));
    handle(TopLeftHandleRole)->setPos(boundingRect().bottomLeft());
    handle(LeftHandleRole)->setPos(QPointF(boundingRect().left(),
                                           boundingRect().center().y()));
    handle(RightHandleRole)->setPos(QPointF(boundingRect().right(),
                                            boundingRect().center().y()));
    handle(BottomRightHandleRole)->setPos(boundingRect().topRight());
    handle(BottomHandleRole)->setPos(QPointF(boundingRect().center().x(),
                                             boundingRect().top()));
    handle(BottomLeftHandleRole)->setPos(boundingRect().topLeft());
}

QRectF GraphicsCircularShapeItem::boundingRect() const
{
    const qreal radius = m_primitive->diameter()/2.0;
    return QRectF(QPointF(-radius, -radius),
                  QPointF(radius, radius));
}

void GraphicsCircularShapeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(widget)

    GraphicsStyleOption itemOption;
    itemOption.exposedRect = option->exposedRect;
    itemOption.palette = layer()->graphicsPalette(featureType());
    itemOption.state = state();

    LeGraphicsStyle *style = graphicsScene()->graphicsStyle();
    style->drawCircle(&itemOption, painter, QPointF(0, 0), m_primitive->diameter());
}

LeGraphicsItem *GraphicsCircularShapeItem::clone() const
{
    return nullptr;
}

void GraphicsCircularShapeItem::addHandles()
{
    addHandle(TopRightHandleRole);
    addHandle(TopHandleRole);
    addHandle(TopLeftHandleRole);
    addHandle(BottomRightHandleRole);
    addHandle(BottomHandleRole);
    addHandle(BottomLeftHandleRole);
    addHandle(RightHandleRole);
    addHandle(LeftHandleRole);
    addHandle(CenterHandleRole);
}

void GraphicsCircularShapeItem::documentObjectAboutToChangeProperty(const LDO::IDocumentObject *object,
                                                                    const QString &name,
                                                                    const QVariant &oldValue)
{
    Q_UNUSED(object)
    Q_UNUSED(oldValue)

    if (name == "diameter")
        prepareGeometryChange();
}

void GraphicsCircularShapeItem::documentObjectPropertyChanged(const LDO::IDocumentObject *object,
                                                              const QString &name,
                                                              const QVariant &newValue)
{
    Q_UNUSED(object)

    if (name == "diameter")
        updateGeometry();
    else if (name == "position")
        setPos(newValue.toPointF());
    else if (name == "rotation")
        setRotation(newValue.toDouble());
    else if (name == "mirrored")
        setTransform(QTransform().scale(newValue.toBool() ? -1 : 1, 1));
}

QPointF GraphicsCircularShapeItem::handlePositionChangeFilter(LeGraphicsHandleRole role,
                                                              const QPointF &value)
{
    switch (role)
    {
        case TopHandleRole:
        {
            const qreal radius = qAbs(value.y());
            m_primitive->setDiameter(2*radius);
            return QPointF(0, radius);
        }
        case BottomHandleRole:
        {
            const qreal radius = qAbs(value.y());
            m_primitive->setDiameter(2*qAbs(value.y()));
            return QPointF(0, -radius);
        }
        case LeftHandleRole:
        {
            const qreal radius = qAbs(value.x());
            m_primitive->setDiameter(2*radius);
            return QPointF(-radius, 0);
        }
        case RightHandleRole:
        {
            const qreal radius = qAbs(value.x());
            m_primitive->setDiameter(2*radius);
            return QPointF(radius, 0);
        }
        case TopRightHandleRole:
        {
            const qreal radius = qMax(qAbs(value.x()), qAbs(value.y()));
            m_primitive->setDiameter(2*radius);
            return QPointF(radius, radius);
        }
        case TopLeftHandleRole:
        {
            const qreal radius = qMax(qAbs(value.x()), qAbs(value.y()));
            m_primitive->setDiameter(2*radius);
            return QPointF(-radius, radius);
        }
        case BottomRightHandleRole:
        {
            const qreal radius = qMax(qAbs(value.x()), qAbs(value.y()));
            m_primitive->setDiameter(2*radius);
            return QPointF(radius, -radius);
        }
        case BottomLeftHandleRole:
        {
            const qreal radius = qMax(qAbs(value.x()), qAbs(value.y()));
            m_primitive->setDiameter(2*radius);
            return QPointF(-radius, -radius);
        }
        case CenterHandleRole:
        {
            // Move the parent to where the handle wants to go...
            m_primitive->setPosition(mapToParent(value));
            // And leave the handle where it should be
            return QPointF(0, 0);
        }
        default:
            return value;
    }
}
